import bpy
import re
from bpy.types import Context, Object, Collection, bpy_prop_collection
from typing import Union
from ..functions.collections import remove_collection_if_is_empty #, remove_collection
from ...addon.naming import FluidLabNaming
import bmesh


def enter_object_mode(context):
    if context.mode != 'OBJECT':
        bpy.ops.object.mode_set(mode='OBJECT', toggle=False)


def enter_edit_mode(context):
    active = context.active_object
    if active:
        if active.type == 'MESH' or active.type == 'CURVE':
            if bpy.context.mode != 'EDIT':
                bpy.ops.object.mode_set(mode='EDIT', toggle=False)
        else:
            print("Is necessary the active object is of the MESH type for enter in edit mode!")
    else:
        print("Is necessary the active object for enter in edit mode!")


def set_active_object(context, obj: Object):
    if not isinstance(obj, Object):
        if isinstance(obj, str):
            if obj in context.view_layer.objects:
                obj = context.view_layer.objects.get(obj)
            else:
                obj = False
                print(obj, 'not avalidable in context.view_layer.objects!')
    if obj:
        if obj.name in context.view_layer.objects:
            context.scene.view_layers[context.view_layer.name].objects.active = obj


def rm_ob(target: Union[str, Object]) -> None:
    obj = None
    data_objects = bpy.data.objects
    if isinstance(target, str):
        obj = data_objects.get(target, None)
    elif isinstance(target, Object):
        obj = target
    else:
        print('[rm_ob]: not valid type obj recived!')
    if obj:
        if obj.name in data_objects:
            data_objects.remove(obj, do_unlink=True)
        else:
            print('[rm_ob]:', obj, 'Not in bpy.data.objects, cant remove object!')
    else:
        print('[rm_ob]:', obj, 'Not valid object, cant remove object!')


def hide_object_in_viewport(obj):
    # ojito
    obj.object.hide_set(True)


def unhide_object_in_viewport(obj):
    # ojito
    obj.object.hide_set(False)


def hide_object_in_render(obj):
    obj.hide_render = True


def unhide_object_in_render(obj):
    obj.hide_render = False


def make_object_unselectable(obj):
    obj.hide_select = True


def make_object_selectable(obj):
    obj.hide_select = False


def has_keyframe(ob, attr):
    anim = ob.animation_data
    if anim is not None and anim.action is not None:
        for fcu in anim.action.fcurves:
            if fcu.data_path == attr:
                return len(fcu.keyframe_points) > 0
    return False


def name_with_zfill(context: Context, item_name: str, place: Collection) -> str:
    
    if isinstance(place, Collection) or isinstance(place, bpy_prop_collection):
        target_place = place.keys()

    elif isinstance(place, list):
        target_place = [p.name for p in place]

    n = 1
    regex_rule_02 = "^(.*)(_|\.)(.*)?(\d*)"
   
    while item_name in target_place:  # CUIDADO, si item_name nunca cambia se podría enbuclar

        # existent_with_same_name = list(filter(lambda ob: ob.name.startswith(item_name), place))
        existent_with_same_name = [ob for ob in place if ob.name.startswith(item_name)]
        totals = str(len(existent_with_same_name)+n)
        zf = max(2, len(totals))  # Asegurarse de que zf sea al menos 2

        match = re.search(regex_rule_02, item_name)
        if match:

            # print("Matches Groups:", match.group(1), match.group(2), match.group(3), match.group(4))

            if match.group(3).isdigit():
                first_string = match.group(1)+match.group(2)
            else:
                first_string = match.group(1)+match.group(2)+match.group(3)
            
            # Elimino los espacios en blanco y los puntos y los sustituyo por underscores:
            first_string = first_string.replace("\ ", "_").replace(".", "_")
            separator = "_" if first_string[-1] != "_" else ""
            item_name = f"{first_string}{separator}{totals.zfill(zf)}"

        else:

            # Si no hace match le agregamos la iteración sin más:
            item_name = item_name + "_" + totals.zfill(zf)
        
        if item_name not in target_place:
            break
        
        n += 1

    # limpieza para evitar nombres con .001
    # si existen colecciones huerfanas de antes, las eliminamos para que no agrege .001 a los nombres:
    coll_to_rm = bpy.data.collections.get(item_name)
    if coll_to_rm:
        remove_collection_if_is_empty(context, coll_to_rm)

    # print("new name:", item_name)
    return item_name


def add_single_vertext_ob(ob_name:str="SingleVertexObject", mesh_name:str="SingleVertexMesh", ob_coords:tuple=(0, 0, 0), vtx_coords:tuple=(0, 0, 0), collection:Collection=None) -> Object:
    
    # Crear un nuevo objeto de tipo mesh
    mesh = bpy.data.meshes.new(name=mesh_name)

    # Crear un nuevo bmesh
    bm = bmesh.new()
    
    # Agregar un vertice al bmesh
    bm_verts = bm.verts.new(vtx_coords)  # Coordenadas del vértice (0, 0, 0)
    
    # Finalizar el bmesh
    bm.to_mesh(mesh)
    bm.free()

    # Crear un nuevo objeto en la escena
    ob = bpy.data.objects.new(ob_name, mesh)
    ob.location = ob_coords

    # Sacamos el objeto del limbo:
    if collection is not None:
        collection.objects.link(ob)

    return ob